Trainer: Ruth Chirinos

5 Transformación de datos

5.1.1 Prerequisitos

#install.packages("dplyr")
library(tidyverse)

5.1.2 vuelos

?vuelos

Ver el tibble (data frame)

View(vuelos)
glimpse(vuelos)
Rows: 336,776
Columns: 19
$ anio               <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 201…
$ mes                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ dia                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ horario_salida     <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558, 55…
$ salida_programada  <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600, 60…
$ atraso_salida      <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -1, …
$ horario_llegada    <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849, 8…
$ llegada_programada <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851, 8…
$ atraso_llegada     <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -14,…
$ aerolinea          <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", "AA…
$ vuelo              <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, 49,…
$ codigo_cola        <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N39463…
$ origen             <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA", "…
$ destino            <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD", "…
$ tiempo_vuelo       <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, 158…
$ distancia          <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733, 10…
$ hora               <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, …
$ minuto             <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, …
$ fecha_hora         <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 05:…

5.2 Filtrar filas con filter()

ene1 <- filter(vuelos, mes == 1, dia == 1)
(dic25 <- filter(vuelos, mes == 12, dia == 25))
NA
5.2.1 Comparaciones

Operadores de comparación: >, >=, <, <=, != (no igual) y == (igual).

filter(vuelos, mes == 1)
#> Error: `mes` (`mes = 1`) must not be named, do you need `==`?
sqrt(2)^2 == 2
[1] FALSE
#> [1] FALSE
1 / 49 * 49 == 1
[1] FALSE
#> [1] FALSE

Utiliza near para números de punto flotante

near(sqrt(2)^2, 2)
[1] TRUE
#> [1] TRUE
near(1 / 49 * 49, 1)
[1] TRUE
#> [1] TRUE

5.2.2 Operadores lógicos

Operadores Booleanos: & es “y”, | es “o”, y ! es “no”

filter(vuelos, mes == 11 | mes == 12)
nov_dic <- filter(vuelos, mes %in% c(11, 12))
filter(vuelos, mes %in% c(11, 12))
filter(vuelos, !(atraso_llegada > 120 | atraso_salida > 120))
filter(vuelos  , atraso_llegada <= 120, atraso_salida <= 120)

5.2.3 Valores faltantes

NA > 5
[1] NA
#> [1] NA
10 == NA
[1] NA
#> [1] NA
NA + 10
[1] NA
#> [1] NA
NA / 2
[1] NA
#> [1] NA
NA == NA
[1] NA
#> [1] NA
x <- NA
# Sea y la edad de Juan. No sabemos qué edad tiene.
y <- NA
# ¿Tienen Juan y María la misma edad?
x == y
[1] NA
# ¡No sabemos!

Verificar si el error es NA

is.na(x)
[1] TRUE
#> [1] TRUE

5.2.4 Ejercicios

Encuentra todos los vuelos que:

Tuvieron un retraso de llegada de dos o más horas

glimpse(vuelos)
Rows: 336,776
Columns: 19
$ anio               <int> 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 2013, 201…
$ mes                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ dia                <int> 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, …
$ horario_salida     <int> 517, 533, 542, 544, 554, 554, 555, 557, 557, 558, 558, 55…
$ salida_programada  <int> 515, 529, 540, 545, 600, 558, 600, 600, 600, 600, 600, 60…
$ atraso_salida      <dbl> 2, 4, 2, -1, -6, -4, -5, -3, -3, -2, -2, -2, -2, -2, -1, …
$ horario_llegada    <int> 830, 850, 923, 1004, 812, 740, 913, 709, 838, 753, 849, 8…
$ llegada_programada <int> 819, 830, 850, 1022, 837, 728, 854, 723, 846, 745, 851, 8…
$ atraso_llegada     <dbl> 11, 20, 33, -18, -25, 12, 19, -14, -8, 8, -2, -3, 7, -14,…
$ aerolinea          <chr> "UA", "UA", "AA", "B6", "DL", "UA", "B6", "EV", "B6", "AA…
$ vuelo              <int> 1545, 1714, 1141, 725, 461, 1696, 507, 5708, 79, 301, 49,…
$ codigo_cola        <chr> "N14228", "N24211", "N619AA", "N804JB", "N668DN", "N39463…
$ origen             <chr> "EWR", "LGA", "JFK", "JFK", "LGA", "EWR", "EWR", "LGA", "…
$ destino            <chr> "IAH", "IAH", "MIA", "BQN", "ATL", "ORD", "FLL", "IAD", "…
$ tiempo_vuelo       <dbl> 227, 227, 160, 183, 116, 150, 158, 53, 140, 138, 149, 158…
$ distancia          <dbl> 1400, 1416, 1089, 1576, 762, 719, 1065, 229, 944, 733, 10…
$ hora               <dbl> 5, 5, 5, 5, 6, 5, 6, 6, 6, 6, 6, 6, 6, 6, 6, 5, 6, 6, 6, …
$ minuto             <dbl> 15, 29, 40, 45, 0, 58, 0, 0, 0, 0, 0, 0, 0, 0, 0, 59, 0, …
$ fecha_hora         <dttm> 2013-01-01 05:00:00, 2013-01-01 05:00:00, 2013-01-01 05:…
filter(vuelos, atraso_llegada >=2)

Volaron a Houston (IAH o HOU)

filter(vuelos,destino=='IAH' | destino == 'HOU')

Fueron operados por United, American o Delta

filter(vuelos, aerolinea=='UA' | aerolinea == 'AA' | aerolinea == 'DL'  )

Partieron en invierno (julio, agosto y septiembre)

filter(vuelos, mes %in% c(7,8,9))

Llegaron más de dos horas tarde, pero no salieron tarde

filter(vuelos, atraso_llegada >2)

Se retrasaron por lo menos una hora, pero repusieron más de 30 minutos en vuelo

Partieron entre la medianoche y las 6 a.m. (incluyente)

Otra función de dplyr que es útil para usar filtros es between(). ¿Qué hace? ¿Puedes usarla para simplificar el código necesario para responder a los desafíos anteriores?

?between
filter(vuelos,  between(horario_salida,000, 600))

¿Cuántos vuelos tienen datos faltantes en horario_salida? ¿Qué otras variables tienen valores faltantes? ¿Qué representan estas filas?

¿Por qué NA ^ 0 no es faltante? ¿Por qué NA | TRUE no es faltante? ¿Por qué FALSE & NA no es faltante? ¿Puedes descubrir la regla general? (¡NA * 0 es un contraejemplo complicado!)

5.3 Reordenar las filas con arrange()

arrange(vuelos, anio, mes, dia)
#> # A tibble: 336,776 x 19
#>    anio   mes   dia horario_salida salida_programa… atraso_salida
#>   <int> <int> <int>          <int>            <int>         <dbl>
#> 1  2013     1     1            517              515             2
#> 2  2013     1     1            533              529             4
#> 3  2013     1     1            542              540             2
#> 4  2013     1     1            544              545            -1
#> 5  2013     1     1            554              600            -6
#> 6  2013     1     1            554              558            -4
#> # … with 336,770 more rows, and 13 more variables: horario_llegada <int>,
#> #   llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
#> #   vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
#> #   tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
#> #   fecha_hora <dttm>
arrange(vuelos, desc(atraso_salida))
#> # A tibble: 336,776 x 19
#>    anio   mes   dia horario_salida salida_programa… atraso_salida
#>   <int> <int> <int>          <int>            <int>         <dbl>
#> 1  2013     1     9            641              900          1301
#> 2  2013     6    15           1432             1935          1137
#> 3  2013     1    10           1121             1635          1126
#> 4  2013     9    20           1139             1845          1014
#> 5  2013     7    22            845             1600          1005
#> 6  2013     4    10           1100             1900           960
#> # … with 336,770 more rows, and 13 more variables: horario_llegada <int>,
#> #   llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
#> #   vuelo <int>, codigo_cola <chr>, origen <chr>, destino <chr>,
#> #   tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
#> #   fecha_hora <dttm>

Los valores faltantes siempre se ordenan al final

df <- tibble(x = c(5, 2, NA))
arrange(df, x)
#> # A tibble: 3 x 1
#>       x
#>   <dbl>
#> 1     2
#> 2     5
#> 3    NA
arrange(df, desc(x))
#> # A tibble: 3 x 1
#>       x
#>   <dbl>
#> 1     5
#> 2     2
#> 3    NA

5.3.1 Ejercicios

¿Cómo podrías usar arrange() para ordenar todos los valores faltantes al comienzo? (Sugerencia: usa is.na()).

arrange(df, !is.na(x))

Ordena vuelos para encontrar los vuelos más retrasados. Encuentra los vuelos que salieron más temprano.

arrange(vuelos, desc(atraso_salida))
arrange(vuelos, salida_programada)

Ordena vuelos para encontrar los vuelos más rápidos (que viajaron a mayor velocidad).

arrange(vuelos, tiempo_vuelo)

¿Cuáles vuelos viajaron más lejos? ¿Cuál viajó más cerca?

arrange(vuelos, desc(distancia))

5.4 Seleccionar columnas con select()

# Seleccionar columnas por nombre
select(vuelos, anio, mes, dia)
#> # A tibble: 336,776 x 3
#>    anio   mes   dia
#>   <int> <int> <int>
#> 1  2013     1     1
#> 2  2013     1     1
#> 3  2013     1     1
#> 4  2013     1     1
#> 5  2013     1     1
#> 6  2013     1     1
#> # … with 336,770 more rows
# Seleccionar todas las columnas entre anio y dia (incluyente)
select(vuelos, anio:dia)
#> # A tibble: 336,776 x 3
#>    anio   mes   dia
#>   <int> <int> <int>
#> 1  2013     1     1
#> 2  2013     1     1
#> 3  2013     1     1
#> 4  2013     1     1
#> 5  2013     1     1
#> 6  2013     1     1
#> # … with 336,770 more rows
# Seleccionar todas las columnas excepto aquellas entre anio en dia (incluyente)
select(vuelos, -(anio:dia))
#> # A tibble: 336,776 x 16
#>   horario_salida salida_programa… atraso_salida horario_llegada llegada_program…
#>            <int>            <int>         <dbl>           <int>            <int>
#> 1            517              515             2             830              819
#> 2            533              529             4             850              830
#> 3            542              540             2             923              850
#> 4            544              545            -1            1004             1022
#> 5            554              600            -6             812              837
#> 6            554              558            -4             740              728
#> # … with 336,770 more rows, and 11 more variables: atraso_llegada <dbl>,
#> #   aerolinea <chr>, vuelo <int>, codigo_cola <chr>, origen <chr>,
#> #   destino <chr>, tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>,
#> #   minuto <dbl>, fecha_hora <dttm>
rename(vuelos, cola_num = codigo_cola)
#> # A tibble: 336,776 x 19
#>    anio   mes   dia horario_salida salida_programa… atraso_salida
#>   <int> <int> <int>          <int>            <int>         <dbl>
#> 1  2013     1     1            517              515             2
#> 2  2013     1     1            533              529             4
#> 3  2013     1     1            542              540             2
#> 4  2013     1     1            544              545            -1
#> 5  2013     1     1            554              600            -6
#> 6  2013     1     1            554              558            -4
#> # … with 336,770 more rows, and 13 more variables: horario_llegada <int>,
#> #   llegada_programada <int>, atraso_llegada <dbl>, aerolinea <chr>,
#> #   vuelo <int>, cola_num <chr>, origen <chr>, destino <chr>,
#> #   tiempo_vuelo <dbl>, distancia <dbl>, hora <dbl>, minuto <dbl>,
#> #   fecha_hora <dttm>
select(vuelos, fecha_hora, tiempo_vuelo, everything())
#> # A tibble: 336,776 x 19
#>   fecha_hora          tiempo_vuelo  anio   mes   dia horario_salida
#>   <dttm>                     <dbl> <int> <int> <int>          <int>
#> 1 2013-01-01 05:00:00          227  2013     1     1            517
#> 2 2013-01-01 05:00:00          227  2013     1     1            533
#> 3 2013-01-01 05:00:00          160  2013     1     1            542
#> 4 2013-01-01 05:00:00          183  2013     1     1            544
#> 5 2013-01-01 06:00:00          116  2013     1     1            554
#> 6 2013-01-01 05:00:00          150  2013     1     1            554
#> # … with 336,770 more rows, and 13 more variables: salida_programada <int>,
#> #   atraso_salida <dbl>, horario_llegada <int>, llegada_programada <int>,
#> #   atraso_llegada <dbl>, aerolinea <chr>, vuelo <int>, codigo_cola <chr>,
#> #   origen <chr>, destino <chr>, distancia <dbl>, hora <dbl>, minuto <dbl>
select(vuelos, anio, anio, mes, mes)

5.5 Añadir nuevas variables con mutate()

vuelos_sml <- select(vuelos,
  anio:dia,
  starts_with("atraso"),
  distancia,
  tiempo_vuelo
)
mutate(vuelos_sml,
  ganancia = atraso_salida - atraso_llegada,
  velocidad = distancia / tiempo_vuelo * 60
)
#> # A tibble: 336,776 x 9
#>    anio   mes   dia atraso_salida atraso_llegada distancia tiempo_vuelo ganancia
#>   <int> <int> <int>         <dbl>          <dbl>     <dbl>        <dbl>    <dbl>
#> 1  2013     1     1             2             11      1400          227       -9
#> 2  2013     1     1             4             20      1416          227      -16
#> 3  2013     1     1             2             33      1089          160      -31
#> 4  2013     1     1            -1            -18      1576          183       17
#> 5  2013     1     1            -6            -25       762          116       19
#> 6  2013     1     1            -4             12       719          150      -16
#> # … with 336,770 more rows, and 1 more variable: velocidad <dbl>
mutate(vuelos_sml,
  ganancia = atraso_salida - atraso_llegada,
  horas = tiempo_vuelo / 60,
  ganacia_por_hora = ganancia / horas
)
#> # A tibble: 336,776 x 10
#>    anio   mes   dia atraso_salida atraso_llegada distancia tiempo_vuelo ganancia
#>   <int> <int> <int>         <dbl>          <dbl>     <dbl>        <dbl>    <dbl>
#> 1  2013     1     1             2             11      1400          227       -9
#> 2  2013     1     1             4             20      1416          227      -16
#> 3  2013     1     1             2             33      1089          160      -31
#> 4  2013     1     1            -1            -18      1576          183       17
#> 5  2013     1     1            -6            -25       762          116       19
#> 6  2013     1     1            -4             12       719          150      -16
#> # … with 336,770 more rows, and 2 more variables: horas <dbl>,
#> #   ganacia_por_hora <dbl>
transmute(vuelos,
  ganancia = atraso_salida - atraso_llegada,
  horas = tiempo_vuelo / 60,
  ganancia_por_hora = ganancia / horas
)
#> # A tibble: 336,776 x 3
#>   ganancia horas ganancia_por_hora
#>      <dbl> <dbl>             <dbl>
#> 1       -9  3.78             -2.38
#> 2      -16  3.78             -4.23
#> 3      -31  2.67            -11.6 
#> 4       17  3.05              5.57
#> 5       19  1.93              9.83
#> 6      -16  2.5              -6.4 
#> # … with 336,770 more rows

5.5.1 Funciones de creación útiles

transmute(vuelos,
  horario_salida,
  hora = horario_salida %/% 100,
  minuto = horario_salida %% 100
)
#> # A tibble: 336,776 x 3
#>   horario_salida  hora minuto
#>            <int> <dbl>  <dbl>
#> 1            517     5     17
#> 2            533     5     33
#> 3            542     5     42
#> 4            544     5     44
#> 5            554     5     54
#> 6            554     5     54
#> # … with 336,770 more rows
transmute(vuelos,
  horario_salida,
  hora = horario_salida %/% 100,
  minuto = horario_salida %% 100
)
#> # A tibble: 336,776 x 3
#>   horario_salida  hora minuto
#>            <int> <dbl>  <dbl>
#> 1            517     5     17
#> 2            533     5     33
#> 3            542     5     42
#> 4            544     5     44
#> 5            554     5     54
#> 6            554     5     54
#> # … with 336,770 more rows
(x <- 1:10)
 [1]  1  2  3  4  5  6  7  8  9 10
#>  [1]  1  2  3  4  5  6  7  8  9 10
lag(x)
 [1] NA  1  2  3  4  5  6  7  8  9
#>  [1] NA  1  2  3  4  5  6  7  8  9
lead(x)
 [1]  2  3  4  5  6  7  8  9 10 NA
#>  [1]  2  3  4  5  6  7  8  9 10 NA
x
 [1]  1  2  3  4  5  6  7  8  9 10
#>  [1]  1  2  3  4  5  6  7  8  9 10
cumsum(x)
 [1]  1  3  6 10 15 21 28 36 45 55
#>  [1]  1  3  6 10 15 21 28 36 45 55
cummean(x)
 [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
#>  [1] 1.0 1.5 2.0 2.5 3.0 3.5 4.0 4.5 5.0 5.5
y <- c (1, 2, 2, NA, 3, 4)
min_rank(y)
[1]  1  2  2 NA  4  5
#> [1]  1  2  2 NA  4  5
min_rank(desc(y))
[1]  5  3  3 NA  2  1
#> [1]  5  3  3 NA  2  1
y <- c (1, 2, 2, NA, 3, 4)
min_rank(y)
[1]  1  2  2 NA  4  5
#> [1]  1  2  2 NA  4  5
min_rank(desc(y))
[1]  5  3  3 NA  2  1
#> [1]  5  3  3 NA  2  1
row_number(y)
[1]  1  2  3 NA  4  5
#> [1]  1  2  3 NA  4  5
dense_rank(y)
[1]  1  2  2 NA  3  4
#> [1]  1  2  2 NA  3  4
percent_rank(y)
[1] 0.00 0.25 0.25   NA 0.75 1.00
#> [1] 0.00 0.25 0.25   NA 0.75 1.00
cume_dist(y)
[1] 0.2 0.6 0.6  NA 0.8 1.0
#> [1] 0.2 0.6 0.6  NA 0.8 1.0

5.6 Resúmenes agrupados con summarise()

summarise(vuelos, atraso = mean(atraso_salida, na.rm = TRUE))
#> # A tibble: 1 x 1
#>   atraso
#>    <dbl>
#> 1   12.6
por_dia <- group_by(vuelos, anio, mes, dia)
summarise(por_dia, atraso = mean(atraso_salida, na.rm = TRUE))

5.6.1 Combinación de múltiples operaciones con el pipe

por_destino <- group_by(vuelos, destino)
atraso <- summarise(por_destino,
  conteo = n(),
  distancia = mean(distancia, na.rm = TRUE),
  atraso = mean(atraso_llegada, na.rm = TRUE)
)
atraso <- filter(atraso, conteo > 20, destino != "HNL")

# Parece que las demoras aumentan con las distancias hasta ~ 750 millas
# y luego disminuyen. ¿Tal vez a medida que los vuelos se hacen más 
# largos, hay más habilidad para compensar las demoras en el aire?

ggplot(data = atraso, mapping = aes(x = distancia, y = atraso)) +
  geom_point(aes(size = conteo), alpha = 1/3) +
  geom_smooth(se = FALSE)

#> `geom_smooth()` using method = 'loess' and formula 'y ~ x'
atrasos <- vuelos %>% 
  group_by(destino) %>% 
  summarise(
    conteo = n(),
    distancia = mean(distancia, na.rm = TRUE),
    atraso = mean(atraso_llegada, na.rm = TRUE)
  ) %>% 
  filter(conteo > 20, destino != "HNL")
atrasos

5.6.2 Valores faltantes

vuelos %>% 
  group_by(anio, mes, dia) %>% 
  summarise(mean = mean(atraso_salida))
vuelos %>% 
  group_by(anio, mes, dia) %>% 
  summarise(mean = mean(atraso_salida, na.rm = TRUE))
no_cancelados <- vuelos %>% 
  filter(!is.na(atraso_salida), !is.na(atraso_llegada))

no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(mean = mean(atraso_salida))

5.6.3 Conteos

atrasos <- no_cancelados %>% 
  group_by(codigo_cola) %>% 
  summarise(
   atraso = mean(atraso_llegada)
  )

ggplot(data = atrasos, mapping = aes(x = atraso)) + 
  geom_freqpoly(binwidth = 10)

atrasos <- no_cancelados %>% 
  group_by(codigo_cola) %>% 
  summarise(
    atraso = mean(atraso_llegada, na.rm = TRUE),
    n = n()
  )

ggplot(data = atrasos, mapping = aes(x = n, y = atraso)) + 
  geom_point(alpha = 1/10)

atrasos %>% 
  filter(n > 25) %>% 
  ggplot(mapping = aes(x = n, y = atraso)) + 
    geom_point(alpha = 1/10)

# Convierte a tibble para puedas imprimirlo de una manera legible
bateo <- as_tibble(datos::bateadores)

rendimiento_bateadores <- bateo %>% 
  group_by(id_jugador) %>% 
  summarise(
    pb = sum(golpes, na.rm = TRUE) / sum(al_bate, na.rm = TRUE),
    ab = sum(al_bate, na.rm = TRUE)
  )

rendimiento_bateadores %>% 
  filter(ab > 100) %>% 
  ggplot(mapping = aes(x = ab, y = pb)) +
  geom_point() +
  geom_smooth(se = FALSE)

#> `geom_smooth()` using method = 'gam' and formula 'y ~ s(x, bs = "cs")'
rendimiento_bateadores %>% 
  arrange(desc(pb))

5.6.4 Funciones de resumen útiles

no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(
    prom_atraso1 = mean(atraso_llegada),
    prom_atraso2 = mean(atraso_llegada[atraso_llegada > 0]) # el promedio de atrasos positivos
  )

Medidas de dispersión: sd(x), IQR(x), mad(x). La raíz de la desviación media al cuadrado o desviación estándar sd(x) es una medida estándar de dispersión. El rango intercuartil IQR() y la desviación media absoluta mad(x) son medidas robustas equivalentes que pueden ser más útiles si tienes valores atípicos.

# ¿Por qué la distancia a algunos destinos es más variable que la de otros?
no_cancelados %>% 
  group_by(destino) %>% 
  summarise(distancia_sd = sd(distancia)) %>% 
  arrange(desc(distancia_sd))
no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(
    primero = min(horario_salida),
    ultimo = max(horario_salida)
  )
no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(
    primera_salida = first(horario_salida), 
    ultima_salida = last(horario_salida)
  )
no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  mutate(r = min_rank(desc(horario_salida))) %>% 
  filter(r %in% range(r))

Conteos: has visto n()

# ¿Qué destinos tienen la mayoría de las aerolíneas?
no_cancelados %>% 
  group_by(destino) %>% 
  summarise(aerolineas = n_distinct(aerolinea)) %>% 
  arrange(desc(aerolineas))
no_cancelados %>% 
  count(destino)
no_cancelados %>% 
  count(codigo_cola, wt = distancia)

Obtener información de los vuelos más atrasones

no_cancelados %>%
  group_by(aerolinea, vuelo, destino) %>%
  summarise(max_atrasos = max(atraso_salida), max_atrasos_llegada = max(atraso_llegada)) %>%
  arrange(desc(max_atrasos), desc(max_atrasos_llegada)) 
NA
NA
# ¿Cuántos vuelos salieron antes de las 5 am? 
# (estos generalmente son vuelos demorados del día anterior)
no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(n_temprano = sum(horario_salida < 500))
# ¿Qué proporción de vuelos se retrasan más de una hora?
no_cancelados %>% 
  group_by(anio, mes, dia) %>% 
  summarise(hora_prop = mean(atraso_llegada > 60))

5.6.5 Agrupación por múltiples variables

diario <- group_by(vuelos, anio, mes, dia)
(por_dia   <- summarise(diario, vuelos = n()))
(por_mes <- summarise(por_dia, vuelos = sum(vuelos)))
(por_anio  <- summarise(por_mes, vuelos = sum(vuelos)))
NA

5.6.6 Desagrupar

diario %>% 
  ungroup() %>%             # ya no está agrupado por fecha
  summarise(vuelos = n())   # todos los vuelos

5.7 Transformaciones agrupadas (y filtros)

Encuentra los peores miembros de cada grupo:

vuelos_sml %>% 
  group_by(anio, mes, dia) %>%
  filter(rank(desc(atraso_llegada)) < 10)

Encuentra todos los grupos más grandes que un determinado umbral:

popular_destinos <- vuelos %>% 
  group_by(destino) %>% 
  filter(n() > 365)
popular_destinos

Estandariza para calcular las métricas por grupo:

popular_destinos %>% 
  filter(atraso_llegada > 0) %>% 
  mutate(prop_atraso = atraso_llegada / sum(atraso_llegada)) %>% 
  select(anio:dia, destino, atraso_llegada, prop_atraso)
LS0tCnRpdGxlOiAiUi1MYWRpZXMgTGEgUGF6ICcgLSBUcmFuc2Zvcm1hY2nDs24gZGUgbG9zIERhdG9zIgpvdXRwdXQ6IGh0bWxfbm90ZWJvb2sKLS0tClRyYWluZXI6IFJ1dGggQ2hpcmlub3MKCgo8aDE+NSBUcmFuc2Zvcm1hY2nDs24gZGUgZGF0b3M8L2gxPgo8aDM+NS4xLjEgUHJlcmVxdWlzaXRvczwvaDM+CmBgYHtyfQojaW5zdGFsbC5wYWNrYWdlcygiZHBseXIiKQpsaWJyYXJ5KHRpZHl2ZXJzZSkKCmBgYAoKPGgzPjUuMS4yIHZ1ZWxvcyA8L2gzPgpgYGB7cn0KP3Z1ZWxvcwpgYGAKClZlciBlbCB0aWJibGUgKGRhdGEgZnJhbWUpCmBgYHtyfQpWaWV3KHZ1ZWxvcykKZ2xpbXBzZSh2dWVsb3MpCmBgYAoKPGgyPjUuMiBGaWx0cmFyIGZpbGFzIGNvbiBmaWx0ZXIoKTwvaDI+CgpgYGB7cn0KZW5lMSA8LSBmaWx0ZXIodnVlbG9zLCBtZXMgPT0gMSwgZGlhID09IDEpCmBgYAoKYGBge3J9CihkaWMyNSA8LSBmaWx0ZXIodnVlbG9zLCBtZXMgPT0gMTIsIGRpYSA9PSAyNSkpCgpgYGAKPC9oMz41LjIuMSBDb21wYXJhY2lvbmVzPC9oMz4KT3BlcmFkb3JlcyBkZSBjb21wYXJhY2nDs246ICAgPiwgPj0sIDwsIDw9LCAhPSAobm8gaWd1YWwpIHkgPT0gKGlndWFsKS4KCmBgYHtyfQpmaWx0ZXIodnVlbG9zLCBtZXMgPT0gMSkKIz4gRXJyb3I6IGBtZXNgIChgbWVzID0gMWApIG11c3Qgbm90IGJlIG5hbWVkLCBkbyB5b3UgbmVlZCBgPT1gPwpgYGAKYGBge3J9CnNxcnQoMileMiA9PSAyCiM+IFsxXSBGQUxTRQoxIC8gNDkgKiA0OSA9PSAxCiM+IFsxXSBGQUxTRQpgYGAKVXRpbGl6YSBuZWFyIHBhcmEgbsO6bWVyb3MgZGUgcHVudG8gZmxvdGFudGUKYGBge3J9Cm5lYXIoc3FydCgyKV4yLCAyKQojPiBbMV0gVFJVRQpuZWFyKDEgLyA0OSAqIDQ5LCAxKQojPiBbMV0gVFJVRQpgYGAKPGgzPjUuMi4yIE9wZXJhZG9yZXMgbMOzZ2ljb3M8L2gzPgpPcGVyYWRvcmVzIEJvb2xlYW5vczogJiBlcyDigJx54oCdLCB8IGVzIOKAnG/igJ0sIHkgISBlcyDigJxub+KAnQpgYGB7cn0KZmlsdGVyKHZ1ZWxvcywgbWVzID09IDExIHwgbWVzID09IDEyKQpgYGAKCmBgYHtyfQpub3ZfZGljIDwtIGZpbHRlcih2dWVsb3MsIG1lcyAlaW4lIGMoMTEsIDEyKSkKZmlsdGVyKHZ1ZWxvcywgbWVzICVpbiUgYygxMSwgMTIpKQpgYGAKCmBgYHtyfQpmaWx0ZXIodnVlbG9zLCAhKGF0cmFzb19sbGVnYWRhID4gMTIwIHwgYXRyYXNvX3NhbGlkYSA+IDEyMCkpCmZpbHRlcih2dWVsb3MgICwgYXRyYXNvX2xsZWdhZGEgPD0gMTIwLCBhdHJhc29fc2FsaWRhIDw9IDEyMCkKYGBgCgo8aDM+NS4yLjMgVmFsb3JlcyBmYWx0YW50ZXM8L2gzPgpgYGB7cn0KTkEgPiA1CjEwID09IE5BCk5BICsgMTAKTkEgLyAyCmBgYAoKYGBge3J9Ck5BID09IE5BCmBgYAoKYGBge3J9CnggPC0gTkEKIyBTZWEgeSBsYSBlZGFkIGRlIEp1YW4uIE5vIHNhYmVtb3MgcXXDqSBlZGFkIHRpZW5lLgp5IDwtIE5BCiMgwr9UaWVuZW4gSnVhbiB5IE1hcsOtYSBsYSBtaXNtYSBlZGFkPwp4ID09IHkKIyDCoU5vIHNhYmVtb3MhCmBgYAoKVmVyaWZpY2FyIHNpIGVsIGVycm9yIGVzIE5BCmBgYHtyfQppcy5uYSh4KQojPiBbMV0gVFJVRQpgYGAKPGgzPjUuMi40IEVqZXJjaWNpb3M8L2gzPgpFbmN1ZW50cmEgdG9kb3MgbG9zIHZ1ZWxvcyBxdWU6CgpUdXZpZXJvbiB1biByZXRyYXNvIGRlIGxsZWdhZGEgZGUgZG9zIG8gbcOhcyBob3JhcwpgYGB7cn0KZ2xpbXBzZSh2dWVsb3MpCmZpbHRlcih2dWVsb3MsIGF0cmFzb19sbGVnYWRhID49MikKYGBgCgpWb2xhcm9uIGEgSG91c3RvbiAoSUFIIG8gSE9VKQpgYGB7cn0KZmlsdGVyKHZ1ZWxvcyxkZXN0aW5vPT0nSUFIJyB8IGRlc3Rpbm8gPT0gJ0hPVScpCmBgYAoKCkZ1ZXJvbiBvcGVyYWRvcyBwb3IgVW5pdGVkLCBBbWVyaWNhbiBvIERlbHRhCmBgYHtyfQpmaWx0ZXIodnVlbG9zLCBhZXJvbGluZWE9PSdVQScgfCBhZXJvbGluZWEgPT0gJ0FBJyB8IGFlcm9saW5lYSA9PSAnREwnICApCmBgYAoKClBhcnRpZXJvbiBlbiBpbnZpZXJubyAoanVsaW8sIGFnb3N0byB5IHNlcHRpZW1icmUpCmBgYHtyfQpmaWx0ZXIodnVlbG9zLCBtZXMgJWluJSBjKDcsOCw5KSkKYGBgCgoKTGxlZ2Fyb24gbcOhcyBkZSBkb3MgaG9yYXMgdGFyZGUsIHBlcm8gbm8gc2FsaWVyb24gdGFyZGUKYGBge3J9CmZpbHRlcih2dWVsb3MsIGF0cmFzb19sbGVnYWRhID4yKQpgYGAKCgpTZSByZXRyYXNhcm9uIHBvciBsbyBtZW5vcyB1bmEgaG9yYSwgcGVybyByZXB1c2llcm9uIG3DoXMgZGUgMzAgbWludXRvcyBlbiB2dWVsbwoKUGFydGllcm9uIGVudHJlIGxhIG1lZGlhbm9jaGUgeSBsYXMgNiBhLm0uIChpbmNsdXllbnRlKQoKT3RyYSBmdW5jacOzbiBkZSBkcGx5ciBxdWUgZXMgw7p0aWwgcGFyYSB1c2FyIGZpbHRyb3MgZXMgYmV0d2VlbigpLiDCv1F1w6kgaGFjZT8gwr9QdWVkZXMgdXNhcmxhIHBhcmEgc2ltcGxpZmljYXIgZWwgY8OzZGlnbyBuZWNlc2FyaW8gcGFyYSByZXNwb25kZXIgYSBsb3MgZGVzYWbDrW9zIGFudGVyaW9yZXM/CmBgYHtyfQo/YmV0d2VlbgpmaWx0ZXIodnVlbG9zLCAgYmV0d2Vlbihob3JhcmlvX3NhbGlkYSwwMDAsIDYwMCkpCmBgYAoKwr9DdcOhbnRvcyB2dWVsb3MgdGllbmVuIGRhdG9zIGZhbHRhbnRlcyBlbiBob3JhcmlvX3NhbGlkYT8gwr9RdcOpIG90cmFzIHZhcmlhYmxlcyB0aWVuZW4gdmFsb3JlcyBmYWx0YW50ZXM/IMK/UXXDqSByZXByZXNlbnRhbiBlc3RhcyBmaWxhcz8KCsK/UG9yIHF1w6kgTkEgXiAwIG5vIGVzIGZhbHRhbnRlPyDCv1BvciBxdcOpIE5BIHwgVFJVRSBubyBlcyBmYWx0YW50ZT8gwr9Qb3IgcXXDqSBGQUxTRSAmIE5BIG5vIGVzIGZhbHRhbnRlPyDCv1B1ZWRlcyBkZXNjdWJyaXIgbGEgcmVnbGEgZ2VuZXJhbD8gKMKhTkEgKiAwIGVzIHVuIGNvbnRyYWVqZW1wbG8gY29tcGxpY2FkbyEpCgoKPGgyPjUuMyBSZW9yZGVuYXIgbGFzIGZpbGFzIGNvbiBhcnJhbmdlKCk8L2gyPgpgYGB7cn0KYXJyYW5nZSh2dWVsb3MsIGFuaW8sIG1lcywgZGlhKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggMTkKIz4gICAgYW5pbyAgIG1lcyAgIGRpYSBob3JhcmlvX3NhbGlkYSBzYWxpZGFfcHJvZ3JhbWHigKYgYXRyYXNvX3NhbGlkYQojPiAgIDxpbnQ+IDxpbnQ+IDxpbnQ+ICAgICAgICAgIDxpbnQ+ICAgICAgICAgICAgPGludD4gICAgICAgICA8ZGJsPgojPiAxICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTE3ICAgICAgICAgICAgICA1MTUgICAgICAgICAgICAgMgojPiAyICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTMzICAgICAgICAgICAgICA1MjkgICAgICAgICAgICAgNAojPiAzICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTQyICAgICAgICAgICAgICA1NDAgICAgICAgICAgICAgMgojPiA0ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTQ0ICAgICAgICAgICAgICA1NDUgICAgICAgICAgICAtMQojPiA1ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTU0ICAgICAgICAgICAgICA2MDAgICAgICAgICAgICAtNgojPiA2ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTU0ICAgICAgICAgICAgICA1NTggICAgICAgICAgICAtNAojPiAjIOKApiB3aXRoIDMzNiw3NzAgbW9yZSByb3dzLCBhbmQgMTMgbW9yZSB2YXJpYWJsZXM6IGhvcmFyaW9fbGxlZ2FkYSA8aW50PiwKIz4gIyAgIGxsZWdhZGFfcHJvZ3JhbWFkYSA8aW50PiwgYXRyYXNvX2xsZWdhZGEgPGRibD4sIGFlcm9saW5lYSA8Y2hyPiwKIz4gIyAgIHZ1ZWxvIDxpbnQ+LCBjb2RpZ29fY29sYSA8Y2hyPiwgb3JpZ2VuIDxjaHI+LCBkZXN0aW5vIDxjaHI+LAojPiAjICAgdGllbXBvX3Z1ZWxvIDxkYmw+LCBkaXN0YW5jaWEgPGRibD4sIGhvcmEgPGRibD4sIG1pbnV0byA8ZGJsPiwKIz4gIyAgIGZlY2hhX2hvcmEgPGR0dG0+CmBgYAoKYGBge3J9CmFycmFuZ2UodnVlbG9zLCBkZXNjKGF0cmFzb19zYWxpZGEpKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggMTkKIz4gICAgYW5pbyAgIG1lcyAgIGRpYSBob3JhcmlvX3NhbGlkYSBzYWxpZGFfcHJvZ3JhbWHigKYgYXRyYXNvX3NhbGlkYQojPiAgIDxpbnQ+IDxpbnQ+IDxpbnQ+ICAgICAgICAgIDxpbnQ+ICAgICAgICAgICAgPGludD4gICAgICAgICA8ZGJsPgojPiAxICAyMDEzICAgICAxICAgICA5ICAgICAgICAgICAgNjQxICAgICAgICAgICAgICA5MDAgICAgICAgICAgMTMwMQojPiAyICAyMDEzICAgICA2ICAgIDE1ICAgICAgICAgICAxNDMyICAgICAgICAgICAgIDE5MzUgICAgICAgICAgMTEzNwojPiAzICAyMDEzICAgICAxICAgIDEwICAgICAgICAgICAxMTIxICAgICAgICAgICAgIDE2MzUgICAgICAgICAgMTEyNgojPiA0ICAyMDEzICAgICA5ICAgIDIwICAgICAgICAgICAxMTM5ICAgICAgICAgICAgIDE4NDUgICAgICAgICAgMTAxNAojPiA1ICAyMDEzICAgICA3ICAgIDIyICAgICAgICAgICAgODQ1ICAgICAgICAgICAgIDE2MDAgICAgICAgICAgMTAwNQojPiA2ICAyMDEzICAgICA0ICAgIDEwICAgICAgICAgICAxMTAwICAgICAgICAgICAgIDE5MDAgICAgICAgICAgIDk2MAojPiAjIOKApiB3aXRoIDMzNiw3NzAgbW9yZSByb3dzLCBhbmQgMTMgbW9yZSB2YXJpYWJsZXM6IGhvcmFyaW9fbGxlZ2FkYSA8aW50PiwKIz4gIyAgIGxsZWdhZGFfcHJvZ3JhbWFkYSA8aW50PiwgYXRyYXNvX2xsZWdhZGEgPGRibD4sIGFlcm9saW5lYSA8Y2hyPiwKIz4gIyAgIHZ1ZWxvIDxpbnQ+LCBjb2RpZ29fY29sYSA8Y2hyPiwgb3JpZ2VuIDxjaHI+LCBkZXN0aW5vIDxjaHI+LAojPiAjICAgdGllbXBvX3Z1ZWxvIDxkYmw+LCBkaXN0YW5jaWEgPGRibD4sIGhvcmEgPGRibD4sIG1pbnV0byA8ZGJsPiwKIz4gIyAgIGZlY2hhX2hvcmEgPGR0dG0+CmBgYApMb3MgdmFsb3JlcyBmYWx0YW50ZXMgc2llbXByZSBzZSBvcmRlbmFuIGFsIGZpbmFsCmBgYHtyfQpkZiA8LSB0aWJibGUoeCA9IGMoNSwgMiwgTkEpKQphcnJhbmdlKGRmLCB4KQojPiAjIEEgdGliYmxlOiAzIHggMQojPiAgICAgICB4CiM+ICAgPGRibD4KIz4gMSAgICAgMgojPiAyICAgICA1CiM+IDMgICAgTkEKYXJyYW5nZShkZiwgZGVzYyh4KSkKIz4gIyBBIHRpYmJsZTogMyB4IDEKIz4gICAgICAgeAojPiAgIDxkYmw+CiM+IDEgICAgIDUKIz4gMiAgICAgMgojPiAzICAgIE5BCmBgYAoKPGgzPjUuMy4xIEVqZXJjaWNpb3M8L2gzPgrCv0PDs21vIHBvZHLDrWFzIHVzYXIgYXJyYW5nZSgpIHBhcmEgb3JkZW5hciB0b2RvcyBsb3MgdmFsb3JlcyBmYWx0YW50ZXMgYWwgY29taWVuem8/IChTdWdlcmVuY2lhOiB1c2EgaXMubmEoKSkuCmBgYHtyfQphcnJhbmdlKGRmLCAhaXMubmEoeCkpCmBgYAoKCk9yZGVuYSB2dWVsb3MgcGFyYSBlbmNvbnRyYXIgbG9zIHZ1ZWxvcyBtw6FzIHJldHJhc2Fkb3MuIEVuY3VlbnRyYSBsb3MgdnVlbG9zIHF1ZSBzYWxpZXJvbiBtw6FzIHRlbXByYW5vLgpgYGB7cn0KYXJyYW5nZSh2dWVsb3MsIGRlc2MoYXRyYXNvX3NhbGlkYSkpCmFycmFuZ2UodnVlbG9zLCBzYWxpZGFfcHJvZ3JhbWFkYSkKYGBgCgoKT3JkZW5hIHZ1ZWxvcyBwYXJhIGVuY29udHJhciBsb3MgdnVlbG9zIG3DoXMgcsOhcGlkb3MgKHF1ZSB2aWFqYXJvbiBhIG1heW9yIHZlbG9jaWRhZCkuCmBgYHtyfQphcnJhbmdlKHZ1ZWxvcywgdGllbXBvX3Z1ZWxvKQpgYGAKCgrCv0N1w6FsZXMgdnVlbG9zIHZpYWphcm9uIG3DoXMgbGVqb3M/IMK/Q3XDoWwgdmlhasOzIG3DoXMgY2VyY2E/CmBgYHtyfQphcnJhbmdlKHZ1ZWxvcywgZGVzYyhkaXN0YW5jaWEpKQpgYGAKPGgyPjUuNCBTZWxlY2Npb25hciBjb2x1bW5hcyBjb24gc2VsZWN0KCkgPC9oMj4KYGBge3J9CiMgU2VsZWNjaW9uYXIgY29sdW1uYXMgcG9yIG5vbWJyZQpzZWxlY3QodnVlbG9zLCBhbmlvLCBtZXMsIGRpYSkKIz4gIyBBIHRpYmJsZTogMzM2LDc3NiB4IDMKIz4gICAgYW5pbyAgIG1lcyAgIGRpYQojPiAgIDxpbnQ+IDxpbnQ+IDxpbnQ+CiM+IDEgIDIwMTMgICAgIDEgICAgIDEKIz4gMiAgMjAxMyAgICAgMSAgICAgMQojPiAzICAyMDEzICAgICAxICAgICAxCiM+IDQgIDIwMTMgICAgIDEgICAgIDEKIz4gNSAgMjAxMyAgICAgMSAgICAgMQojPiA2ICAyMDEzICAgICAxICAgICAxCiM+ICMg4oCmIHdpdGggMzM2LDc3MCBtb3JlIHJvd3MKIyBTZWxlY2Npb25hciB0b2RhcyBsYXMgY29sdW1uYXMgZW50cmUgYW5pbyB5IGRpYSAoaW5jbHV5ZW50ZSkKc2VsZWN0KHZ1ZWxvcywgYW5pbzpkaWEpCiM+ICMgQSB0aWJibGU6IDMzNiw3NzYgeCAzCiM+ICAgIGFuaW8gICBtZXMgICBkaWEKIz4gICA8aW50PiA8aW50PiA8aW50PgojPiAxICAyMDEzICAgICAxICAgICAxCiM+IDIgIDIwMTMgICAgIDEgICAgIDEKIz4gMyAgMjAxMyAgICAgMSAgICAgMQojPiA0ICAyMDEzICAgICAxICAgICAxCiM+IDUgIDIwMTMgICAgIDEgICAgIDEKIz4gNiAgMjAxMyAgICAgMSAgICAgMQojPiAjIOKApiB3aXRoIDMzNiw3NzAgbW9yZSByb3dzCiMgU2VsZWNjaW9uYXIgdG9kYXMgbGFzIGNvbHVtbmFzIGV4Y2VwdG8gYXF1ZWxsYXMgZW50cmUgYW5pbyBlbiBkaWEgKGluY2x1eWVudGUpCnNlbGVjdCh2dWVsb3MsIC0oYW5pbzpkaWEpKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggMTYKIz4gICBob3JhcmlvX3NhbGlkYSBzYWxpZGFfcHJvZ3JhbWHigKYgYXRyYXNvX3NhbGlkYSBob3JhcmlvX2xsZWdhZGEgbGxlZ2FkYV9wcm9ncmFt4oCmCiM+ICAgICAgICAgICAgPGludD4gICAgICAgICAgICA8aW50PiAgICAgICAgIDxkYmw+ICAgICAgICAgICA8aW50PiAgICAgICAgICAgIDxpbnQ+CiM+IDEgICAgICAgICAgICA1MTcgICAgICAgICAgICAgIDUxNSAgICAgICAgICAgICAyICAgICAgICAgICAgIDgzMCAgICAgICAgICAgICAgODE5CiM+IDIgICAgICAgICAgICA1MzMgICAgICAgICAgICAgIDUyOSAgICAgICAgICAgICA0ICAgICAgICAgICAgIDg1MCAgICAgICAgICAgICAgODMwCiM+IDMgICAgICAgICAgICA1NDIgICAgICAgICAgICAgIDU0MCAgICAgICAgICAgICAyICAgICAgICAgICAgIDkyMyAgICAgICAgICAgICAgODUwCiM+IDQgICAgICAgICAgICA1NDQgICAgICAgICAgICAgIDU0NSAgICAgICAgICAgIC0xICAgICAgICAgICAgMTAwNCAgICAgICAgICAgICAxMDIyCiM+IDUgICAgICAgICAgICA1NTQgICAgICAgICAgICAgIDYwMCAgICAgICAgICAgIC02ICAgICAgICAgICAgIDgxMiAgICAgICAgICAgICAgODM3CiM+IDYgICAgICAgICAgICA1NTQgICAgICAgICAgICAgIDU1OCAgICAgICAgICAgIC00ICAgICAgICAgICAgIDc0MCAgICAgICAgICAgICAgNzI4CiM+ICMg4oCmIHdpdGggMzM2LDc3MCBtb3JlIHJvd3MsIGFuZCAxMSBtb3JlIHZhcmlhYmxlczogYXRyYXNvX2xsZWdhZGEgPGRibD4sCiM+ICMgICBhZXJvbGluZWEgPGNocj4sIHZ1ZWxvIDxpbnQ+LCBjb2RpZ29fY29sYSA8Y2hyPiwgb3JpZ2VuIDxjaHI+LAojPiAjICAgZGVzdGlubyA8Y2hyPiwgdGllbXBvX3Z1ZWxvIDxkYmw+LCBkaXN0YW5jaWEgPGRibD4sIGhvcmEgPGRibD4sCiM+ICMgICBtaW51dG8gPGRibD4sIGZlY2hhX2hvcmEgPGR0dG0+CmBgYApgYGB7cn0KcmVuYW1lKHZ1ZWxvcywgY29sYV9udW0gPSBjb2RpZ29fY29sYSkKIz4gIyBBIHRpYmJsZTogMzM2LDc3NiB4IDE5CiM+ICAgIGFuaW8gICBtZXMgICBkaWEgaG9yYXJpb19zYWxpZGEgc2FsaWRhX3Byb2dyYW1h4oCmIGF0cmFzb19zYWxpZGEKIz4gICA8aW50PiA8aW50PiA8aW50PiAgICAgICAgICA8aW50PiAgICAgICAgICAgIDxpbnQ+ICAgICAgICAgPGRibD4KIz4gMSAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDUxNyAgICAgICAgICAgICAgNTE1ICAgICAgICAgICAgIDIKIz4gMiAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDUzMyAgICAgICAgICAgICAgNTI5ICAgICAgICAgICAgIDQKIz4gMyAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDU0MiAgICAgICAgICAgICAgNTQwICAgICAgICAgICAgIDIKIz4gNCAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDU0NCAgICAgICAgICAgICAgNTQ1ICAgICAgICAgICAgLTEKIz4gNSAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDU1NCAgICAgICAgICAgICAgNjAwICAgICAgICAgICAgLTYKIz4gNiAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDU1NCAgICAgICAgICAgICAgNTU4ICAgICAgICAgICAgLTQKIz4gIyDigKYgd2l0aCAzMzYsNzcwIG1vcmUgcm93cywgYW5kIDEzIG1vcmUgdmFyaWFibGVzOiBob3JhcmlvX2xsZWdhZGEgPGludD4sCiM+ICMgICBsbGVnYWRhX3Byb2dyYW1hZGEgPGludD4sIGF0cmFzb19sbGVnYWRhIDxkYmw+LCBhZXJvbGluZWEgPGNocj4sCiM+ICMgICB2dWVsbyA8aW50PiwgY29sYV9udW0gPGNocj4sIG9yaWdlbiA8Y2hyPiwgZGVzdGlubyA8Y2hyPiwKIz4gIyAgIHRpZW1wb192dWVsbyA8ZGJsPiwgZGlzdGFuY2lhIDxkYmw+LCBob3JhIDxkYmw+LCBtaW51dG8gPGRibD4sCiM+ICMgICBmZWNoYV9ob3JhIDxkdHRtPgpgYGAKYGBge3J9CnNlbGVjdCh2dWVsb3MsIGZlY2hhX2hvcmEsIHRpZW1wb192dWVsbywgZXZlcnl0aGluZygpKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggMTkKIz4gICBmZWNoYV9ob3JhICAgICAgICAgIHRpZW1wb192dWVsbyAgYW5pbyAgIG1lcyAgIGRpYSBob3JhcmlvX3NhbGlkYQojPiAgIDxkdHRtPiAgICAgICAgICAgICAgICAgICAgIDxkYmw+IDxpbnQ+IDxpbnQ+IDxpbnQ+ICAgICAgICAgIDxpbnQ+CiM+IDEgMjAxMy0wMS0wMSAwNTowMDowMCAgICAgICAgICAyMjcgIDIwMTMgICAgIDEgICAgIDEgICAgICAgICAgICA1MTcKIz4gMiAyMDEzLTAxLTAxIDA1OjAwOjAwICAgICAgICAgIDIyNyAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDUzMwojPiAzIDIwMTMtMDEtMDEgMDU6MDA6MDAgICAgICAgICAgMTYwICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTQyCiM+IDQgMjAxMy0wMS0wMSAwNTowMDowMCAgICAgICAgICAxODMgIDIwMTMgICAgIDEgICAgIDEgICAgICAgICAgICA1NDQKIz4gNSAyMDEzLTAxLTAxIDA2OjAwOjAwICAgICAgICAgIDExNiAgMjAxMyAgICAgMSAgICAgMSAgICAgICAgICAgIDU1NAojPiA2IDIwMTMtMDEtMDEgMDU6MDA6MDAgICAgICAgICAgMTUwICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgNTU0CiM+ICMg4oCmIHdpdGggMzM2LDc3MCBtb3JlIHJvd3MsIGFuZCAxMyBtb3JlIHZhcmlhYmxlczogc2FsaWRhX3Byb2dyYW1hZGEgPGludD4sCiM+ICMgICBhdHJhc29fc2FsaWRhIDxkYmw+LCBob3JhcmlvX2xsZWdhZGEgPGludD4sIGxsZWdhZGFfcHJvZ3JhbWFkYSA8aW50PiwKIz4gIyAgIGF0cmFzb19sbGVnYWRhIDxkYmw+LCBhZXJvbGluZWEgPGNocj4sIHZ1ZWxvIDxpbnQ+LCBjb2RpZ29fY29sYSA8Y2hyPiwKIz4gIyAgIG9yaWdlbiA8Y2hyPiwgZGVzdGlubyA8Y2hyPiwgZGlzdGFuY2lhIDxkYmw+LCBob3JhIDxkYmw+LCBtaW51dG8gPGRibD4KYGBgCmBgYHtyfQpzZWxlY3QodnVlbG9zLCBhbmlvLCBhbmlvLCBtZXMsIG1lcykKYGBgCgo8aDI+NS41IEHDsWFkaXIgbnVldmFzIHZhcmlhYmxlcyBjb24gbXV0YXRlKCk8L2gyPgpgYGB7cn0KdnVlbG9zX3NtbCA8LSBzZWxlY3QodnVlbG9zLAogIGFuaW86ZGlhLAogIHN0YXJ0c193aXRoKCJhdHJhc28iKSwKICBkaXN0YW5jaWEsCiAgdGllbXBvX3Z1ZWxvCikKbXV0YXRlKHZ1ZWxvc19zbWwsCiAgZ2FuYW5jaWEgPSBhdHJhc29fc2FsaWRhIC0gYXRyYXNvX2xsZWdhZGEsCiAgdmVsb2NpZGFkID0gZGlzdGFuY2lhIC8gdGllbXBvX3Z1ZWxvICogNjAKKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggOQojPiAgICBhbmlvICAgbWVzICAgZGlhIGF0cmFzb19zYWxpZGEgYXRyYXNvX2xsZWdhZGEgZGlzdGFuY2lhIHRpZW1wb192dWVsbyBnYW5hbmNpYQojPiAgIDxpbnQ+IDxpbnQ+IDxpbnQ+ICAgICAgICAgPGRibD4gICAgICAgICAgPGRibD4gICAgIDxkYmw+ICAgICAgICA8ZGJsPiAgICA8ZGJsPgojPiAxICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDIgICAgICAgICAgICAgMTEgICAgICAxNDAwICAgICAgICAgIDIyNyAgICAgICAtOQojPiAyICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDQgICAgICAgICAgICAgMjAgICAgICAxNDE2ICAgICAgICAgIDIyNyAgICAgIC0xNgojPiAzICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDIgICAgICAgICAgICAgMzMgICAgICAxMDg5ICAgICAgICAgIDE2MCAgICAgIC0zMQojPiA0ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTEgICAgICAgICAgICAtMTggICAgICAxNTc2ICAgICAgICAgIDE4MyAgICAgICAxNwojPiA1ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTYgICAgICAgICAgICAtMjUgICAgICAgNzYyICAgICAgICAgIDExNiAgICAgICAxOQojPiA2ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTQgICAgICAgICAgICAgMTIgICAgICAgNzE5ICAgICAgICAgIDE1MCAgICAgIC0xNgojPiAjIOKApiB3aXRoIDMzNiw3NzAgbW9yZSByb3dzLCBhbmQgMSBtb3JlIHZhcmlhYmxlOiB2ZWxvY2lkYWQgPGRibD4KYGBgCgpgYGB7cn0KbXV0YXRlKHZ1ZWxvc19zbWwsCiAgZ2FuYW5jaWEgPSBhdHJhc29fc2FsaWRhIC0gYXRyYXNvX2xsZWdhZGEsCiAgaG9yYXMgPSB0aWVtcG9fdnVlbG8gLyA2MCwKICBnYW5hY2lhX3Bvcl9ob3JhID0gZ2FuYW5jaWEgLyBob3JhcwopCiM+ICMgQSB0aWJibGU6IDMzNiw3NzYgeCAxMAojPiAgICBhbmlvICAgbWVzICAgZGlhIGF0cmFzb19zYWxpZGEgYXRyYXNvX2xsZWdhZGEgZGlzdGFuY2lhIHRpZW1wb192dWVsbyBnYW5hbmNpYQojPiAgIDxpbnQ+IDxpbnQ+IDxpbnQ+ICAgICAgICAgPGRibD4gICAgICAgICAgPGRibD4gICAgIDxkYmw+ICAgICAgICA8ZGJsPiAgICA8ZGJsPgojPiAxICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDIgICAgICAgICAgICAgMTEgICAgICAxNDAwICAgICAgICAgIDIyNyAgICAgICAtOQojPiAyICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDQgICAgICAgICAgICAgMjAgICAgICAxNDE2ICAgICAgICAgIDIyNyAgICAgIC0xNgojPiAzICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgIDIgICAgICAgICAgICAgMzMgICAgICAxMDg5ICAgICAgICAgIDE2MCAgICAgIC0zMQojPiA0ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTEgICAgICAgICAgICAtMTggICAgICAxNTc2ICAgICAgICAgIDE4MyAgICAgICAxNwojPiA1ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTYgICAgICAgICAgICAtMjUgICAgICAgNzYyICAgICAgICAgIDExNiAgICAgICAxOQojPiA2ICAyMDEzICAgICAxICAgICAxICAgICAgICAgICAgLTQgICAgICAgICAgICAgMTIgICAgICAgNzE5ICAgICAgICAgIDE1MCAgICAgIC0xNgojPiAjIOKApiB3aXRoIDMzNiw3NzAgbW9yZSByb3dzLCBhbmQgMiBtb3JlIHZhcmlhYmxlczogaG9yYXMgPGRibD4sCiM+ICMgICBnYW5hY2lhX3Bvcl9ob3JhIDxkYmw+CmBgYAoKYGBge3J9CnRyYW5zbXV0ZSh2dWVsb3MsCiAgZ2FuYW5jaWEgPSBhdHJhc29fc2FsaWRhIC0gYXRyYXNvX2xsZWdhZGEsCiAgaG9yYXMgPSB0aWVtcG9fdnVlbG8gLyA2MCwKICBnYW5hbmNpYV9wb3JfaG9yYSA9IGdhbmFuY2lhIC8gaG9yYXMKKQojPiAjIEEgdGliYmxlOiAzMzYsNzc2IHggMwojPiAgIGdhbmFuY2lhIGhvcmFzIGdhbmFuY2lhX3Bvcl9ob3JhCiM+ICAgICAgPGRibD4gPGRibD4gICAgICAgICAgICAgPGRibD4KIz4gMSAgICAgICAtOSAgMy43OCAgICAgICAgICAgICAtMi4zOAojPiAyICAgICAgLTE2ICAzLjc4ICAgICAgICAgICAgIC00LjIzCiM+IDMgICAgICAtMzEgIDIuNjcgICAgICAgICAgICAtMTEuNiAKIz4gNCAgICAgICAxNyAgMy4wNSAgICAgICAgICAgICAgNS41NwojPiA1ICAgICAgIDE5ICAxLjkzICAgICAgICAgICAgICA5LjgzCiM+IDYgICAgICAtMTYgIDIuNSAgICAgICAgICAgICAgLTYuNCAKIz4gIyDigKYgd2l0aCAzMzYsNzcwIG1vcmUgcm93cwpgYGAKPGgzPjUuNS4xIEZ1bmNpb25lcyBkZSBjcmVhY2nDs24gw7p0aWxlczwvaDM+CmBgYHtyfQp0cmFuc211dGUodnVlbG9zLAogIGhvcmFyaW9fc2FsaWRhLAogIGhvcmEgPSBob3JhcmlvX3NhbGlkYSAlLyUgMTAwLAogIG1pbnV0byA9IGhvcmFyaW9fc2FsaWRhICUlIDEwMAopCiM+ICMgQSB0aWJibGU6IDMzNiw3NzYgeCAzCiM+ICAgaG9yYXJpb19zYWxpZGEgIGhvcmEgbWludXRvCiM+ICAgICAgICAgICAgPGludD4gPGRibD4gIDxkYmw+CiM+IDEgICAgICAgICAgICA1MTcgICAgIDUgICAgIDE3CiM+IDIgICAgICAgICAgICA1MzMgICAgIDUgICAgIDMzCiM+IDMgICAgICAgICAgICA1NDIgICAgIDUgICAgIDQyCiM+IDQgICAgICAgICAgICA1NDQgICAgIDUgICAgIDQ0CiM+IDUgICAgICAgICAgICA1NTQgICAgIDUgICAgIDU0CiM+IDYgICAgICAgICAgICA1NTQgICAgIDUgICAgIDU0CiM+ICMg4oCmIHdpdGggMzM2LDc3MCBtb3JlIHJvd3MKYGBgCgoKYGBge3J9CnRyYW5zbXV0ZSh2dWVsb3MsCiAgaG9yYXJpb19zYWxpZGEsCiAgaG9yYSA9IGhvcmFyaW9fc2FsaWRhICUvJSAxMDAsCiAgbWludXRvID0gaG9yYXJpb19zYWxpZGEgJSUgMTAwCikKIz4gIyBBIHRpYmJsZTogMzM2LDc3NiB4IDMKIz4gICBob3JhcmlvX3NhbGlkYSAgaG9yYSBtaW51dG8KIz4gICAgICAgICAgICA8aW50PiA8ZGJsPiAgPGRibD4KIz4gMSAgICAgICAgICAgIDUxNyAgICAgNSAgICAgMTcKIz4gMiAgICAgICAgICAgIDUzMyAgICAgNSAgICAgMzMKIz4gMyAgICAgICAgICAgIDU0MiAgICAgNSAgICAgNDIKIz4gNCAgICAgICAgICAgIDU0NCAgICAgNSAgICAgNDQKIz4gNSAgICAgICAgICAgIDU1NCAgICAgNSAgICAgNTQKIz4gNiAgICAgICAgICAgIDU1NCAgICAgNSAgICAgNTQKIz4gIyDigKYgd2l0aCAzMzYsNzcwIG1vcmUgcm93cwpgYGAKYGBge3J9Cih4IDwtIDE6MTApCiM+ICBbMV0gIDEgIDIgIDMgIDQgIDUgIDYgIDcgIDggIDkgMTAKbGFnKHgpCiM+ICBbMV0gTkEgIDEgIDIgIDMgIDQgIDUgIDYgIDcgIDggIDkKbGVhZCh4KQojPiAgWzFdICAyICAzICA0ICA1ICA2ICA3ICA4ICA5IDEwIE5BCmBgYApgYGB7cn0KeAojPiAgWzFdICAxICAyICAzICA0ICA1ICA2ICA3ICA4ICA5IDEwCmN1bXN1bSh4KQojPiAgWzFdICAxICAzICA2IDEwIDE1IDIxIDI4IDM2IDQ1IDU1CmN1bW1lYW4oeCkKIz4gIFsxXSAxLjAgMS41IDIuMCAyLjUgMy4wIDMuNSA0LjAgNC41IDUuMCA1LjUKYGBgCgpgYGB7cn0KeSA8LSBjICgxLCAyLCAyLCBOQSwgMywgNCkKbWluX3JhbmsoeSkKIz4gWzFdICAxICAyICAyIE5BICA0ICA1Cm1pbl9yYW5rKGRlc2MoeSkpCiM+IFsxXSAgNSAgMyAgMyBOQSAgMiAgMQpgYGAKCmBgYHtyfQp5IDwtIGMgKDEsIDIsIDIsIE5BLCAzLCA0KQptaW5fcmFuayh5KQojPiBbMV0gIDEgIDIgIDIgTkEgIDQgIDUKbWluX3JhbmsoZGVzYyh5KSkKIz4gWzFdICA1ICAzICAzIE5BICAyICAxCmBgYAoKYGBge3J9CnJvd19udW1iZXIoeSkKIz4gWzFdICAxICAyICAzIE5BICA0ICA1CmRlbnNlX3JhbmsoeSkKIz4gWzFdICAxICAyICAyIE5BICAzICA0CnBlcmNlbnRfcmFuayh5KQojPiBbMV0gMC4wMCAwLjI1IDAuMjUgICBOQSAwLjc1IDEuMDAKY3VtZV9kaXN0KHkpCiM+IFsxXSAwLjIgMC42IDAuNiAgTkEgMC44IDEuMApgYGAKCjxoMj41LjYgUmVzw7ptZW5lcyBhZ3J1cGFkb3MgY29uIHN1bW1hcmlzZSgpPC9oMj4KYGBge3J9CnN1bW1hcmlzZSh2dWVsb3MsIGF0cmFzbyA9IG1lYW4oYXRyYXNvX3NhbGlkYSwgbmEucm0gPSBUUlVFKSkKIz4gIyBBIHRpYmJsZTogMSB4IDEKIz4gICBhdHJhc28KIz4gICAgPGRibD4KIz4gMSAgIDEyLjYKYGBgCmBgYHtyfQpwb3JfZGlhIDwtIGdyb3VwX2J5KHZ1ZWxvcywgYW5pbywgbWVzLCBkaWEpCnN1bW1hcmlzZShwb3JfZGlhLCBhdHJhc28gPSBtZWFuKGF0cmFzb19zYWxpZGEsIG5hLnJtID0gVFJVRSkpCmBgYAoKPGgzPjUuNi4xIENvbWJpbmFjacOzbiBkZSBtw7psdGlwbGVzIG9wZXJhY2lvbmVzIGNvbiBlbCBwaXBlPC9oMz4KYGBge3J9CnBvcl9kZXN0aW5vIDwtIGdyb3VwX2J5KHZ1ZWxvcywgZGVzdGlubykKYXRyYXNvIDwtIHN1bW1hcmlzZShwb3JfZGVzdGlubywKICBjb250ZW8gPSBuKCksCiAgZGlzdGFuY2lhID0gbWVhbihkaXN0YW5jaWEsIG5hLnJtID0gVFJVRSksCiAgYXRyYXNvID0gbWVhbihhdHJhc29fbGxlZ2FkYSwgbmEucm0gPSBUUlVFKQopCmF0cmFzbyA8LSBmaWx0ZXIoYXRyYXNvLCBjb250ZW8gPiAyMCwgZGVzdGlubyAhPSAiSE5MIikKCiMgUGFyZWNlIHF1ZSBsYXMgZGVtb3JhcyBhdW1lbnRhbiBjb24gbGFzIGRpc3RhbmNpYXMgaGFzdGEgfiA3NTAgbWlsbGFzCiMgeSBsdWVnbyBkaXNtaW51eWVuLiDCv1RhbCB2ZXogYSBtZWRpZGEgcXVlIGxvcyB2dWVsb3Mgc2UgaGFjZW4gbcOhcyAKIyBsYXJnb3MsIGhheSBtw6FzIGhhYmlsaWRhZCBwYXJhIGNvbXBlbnNhciBsYXMgZGVtb3JhcyBlbiBlbCBhaXJlPwoKZ2dwbG90KGRhdGEgPSBhdHJhc28sIG1hcHBpbmcgPSBhZXMoeCA9IGRpc3RhbmNpYSwgeSA9IGF0cmFzbykpICsKICBnZW9tX3BvaW50KGFlcyhzaXplID0gY29udGVvKSwgYWxwaGEgPSAxLzMpICsKICBnZW9tX3Ntb290aChzZSA9IEZBTFNFKQojPiBgZ2VvbV9zbW9vdGgoKWAgdXNpbmcgbWV0aG9kID0gJ2xvZXNzJyBhbmQgZm9ybXVsYSAneSB+IHgnCmBgYAoKCmBgYHtyfQphdHJhc29zIDwtIHZ1ZWxvcyAlPiUgCiAgZ3JvdXBfYnkoZGVzdGlubykgJT4lIAogIHN1bW1hcmlzZSgKICAgIGNvbnRlbyA9IG4oKSwKICAgIGRpc3RhbmNpYSA9IG1lYW4oZGlzdGFuY2lhLCBuYS5ybSA9IFRSVUUpLAogICAgYXRyYXNvID0gbWVhbihhdHJhc29fbGxlZ2FkYSwgbmEucm0gPSBUUlVFKQogICkgJT4lIAogIGZpbHRlcihjb250ZW8gPiAyMCwgZGVzdGlubyAhPSAiSE5MIikKYXRyYXNvcwpgYGAKCjxoMz41LjYuMiBWYWxvcmVzIGZhbHRhbnRlczwvaDM+CmBgYHtyfQp2dWVsb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGF0cmFzb19zYWxpZGEpKQpgYGAKCmBgYHtyfQp2dWVsb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgc3VtbWFyaXNlKG1lYW4gPSBtZWFuKGF0cmFzb19zYWxpZGEsIG5hLnJtID0gVFJVRSkpCmBgYApgYGB7cn0Kbm9fY2FuY2VsYWRvcyA8LSB2dWVsb3MgJT4lIAogIGZpbHRlcighaXMubmEoYXRyYXNvX3NhbGlkYSksICFpcy5uYShhdHJhc29fbGxlZ2FkYSkpCgpub19jYW5jZWxhZG9zICU+JSAKICBncm91cF9ieShhbmlvLCBtZXMsIGRpYSkgJT4lIAogIHN1bW1hcmlzZShtZWFuID0gbWVhbihhdHJhc29fc2FsaWRhKSkKYGBgCgo8aDM+NS42LjMgQ29udGVvczwvaDM+CgpgYGB7cn0KYXRyYXNvcyA8LSBub19jYW5jZWxhZG9zICU+JSAKICBncm91cF9ieShjb2RpZ29fY29sYSkgJT4lIAogIHN1bW1hcmlzZSgKICAgYXRyYXNvID0gbWVhbihhdHJhc29fbGxlZ2FkYSkKICApCgpnZ3Bsb3QoZGF0YSA9IGF0cmFzb3MsIG1hcHBpbmcgPSBhZXMoeCA9IGF0cmFzbykpICsgCiAgZ2VvbV9mcmVxcG9seShiaW53aWR0aCA9IDEwKQpgYGAKCmBgYHtyfQphdHJhc29zIDwtIG5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGNvZGlnb19jb2xhKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgYXRyYXNvID0gbWVhbihhdHJhc29fbGxlZ2FkYSwgbmEucm0gPSBUUlVFKSwKICAgIG4gPSBuKCkKICApCgpnZ3Bsb3QoZGF0YSA9IGF0cmFzb3MsIG1hcHBpbmcgPSBhZXMoeCA9IG4sIHkgPSBhdHJhc28pKSArIAogIGdlb21fcG9pbnQoYWxwaGEgPSAxLzEwKQpgYGAKYGBge3J9CmF0cmFzb3MgJT4lIAogIGZpbHRlcihuID4gMjUpICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gbiwgeSA9IGF0cmFzbykpICsgCiAgICBnZW9tX3BvaW50KGFscGhhID0gMS8xMCkKYGBgCgoKYGBge3J9CiMgQ29udmllcnRlIGEgdGliYmxlIHBhcmEgcHVlZGFzIGltcHJpbWlybG8gZGUgdW5hIG1hbmVyYSBsZWdpYmxlCmJhdGVvIDwtIGFzX3RpYmJsZShkYXRvczo6YmF0ZWFkb3JlcykKCnJlbmRpbWllbnRvX2JhdGVhZG9yZXMgPC0gYmF0ZW8gJT4lIAogIGdyb3VwX2J5KGlkX2p1Z2Fkb3IpICU+JSAKICBzdW1tYXJpc2UoCiAgICBwYiA9IHN1bShnb2xwZXMsIG5hLnJtID0gVFJVRSkgLyBzdW0oYWxfYmF0ZSwgbmEucm0gPSBUUlVFKSwKICAgIGFiID0gc3VtKGFsX2JhdGUsIG5hLnJtID0gVFJVRSkKICApCgpyZW5kaW1pZW50b19iYXRlYWRvcmVzICU+JSAKICBmaWx0ZXIoYWIgPiAxMDApICU+JSAKICBnZ3Bsb3QobWFwcGluZyA9IGFlcyh4ID0gYWIsIHkgPSBwYikpICsKICBnZW9tX3BvaW50KCkgKwogIGdlb21fc21vb3RoKHNlID0gRkFMU0UpCiM+IGBnZW9tX3Ntb290aCgpYCB1c2luZyBtZXRob2QgPSAnZ2FtJyBhbmQgZm9ybXVsYSAneSB+IHMoeCwgYnMgPSAiY3MiKScKYGBgCgpgYGB7cn0KcmVuZGltaWVudG9fYmF0ZWFkb3JlcyAlPiUgCiAgYXJyYW5nZShkZXNjKHBiKSkKYGBgCgo8aDM+IDUuNi40IEZ1bmNpb25lcyBkZSByZXN1bWVuIMO6dGlsZXMgPC9oMz4KYGBge3J9Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgcHJvbV9hdHJhc28xID0gbWVhbihhdHJhc29fbGxlZ2FkYSksCiAgICBwcm9tX2F0cmFzbzIgPSBtZWFuKGF0cmFzb19sbGVnYWRhW2F0cmFzb19sbGVnYWRhID4gMF0pICMgZWwgcHJvbWVkaW8gZGUgYXRyYXNvcyBwb3NpdGl2b3MKICApCmBgYAoKTWVkaWRhcyBkZSBkaXNwZXJzacOzbjogc2QoeCksIElRUih4KSwgbWFkKHgpLiBMYSByYcOteiBkZSBsYSBkZXN2aWFjacOzbiBtZWRpYSBhbCBjdWFkcmFkbyBvIGRlc3ZpYWNpw7NuIGVzdMOhbmRhciBzZCh4KSBlcyB1bmEgbWVkaWRhIGVzdMOhbmRhciBkZSBkaXNwZXJzacOzbi4gRWwgcmFuZ28gaW50ZXJjdWFydGlsIElRUigpIHkgbGEgZGVzdmlhY2nDs24gbWVkaWEgYWJzb2x1dGEgbWFkKHgpIHNvbiBtZWRpZGFzIHJvYnVzdGFzIGVxdWl2YWxlbnRlcyBxdWUgcHVlZGVuIHNlciBtw6FzIMO6dGlsZXMgc2kgdGllbmVzIHZhbG9yZXMgYXTDrXBpY29zLgpgYGB7cn0KIyDCv1BvciBxdcOpIGxhIGRpc3RhbmNpYSBhIGFsZ3Vub3MgZGVzdGlub3MgZXMgbcOhcyB2YXJpYWJsZSBxdWUgbGEgZGUgb3Ryb3M/Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGRlc3Rpbm8pICU+JSAKICBzdW1tYXJpc2UoZGlzdGFuY2lhX3NkID0gc2QoZGlzdGFuY2lhKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhkaXN0YW5jaWFfc2QpKQpgYGAKCmBgYHtyfQpub19jYW5jZWxhZG9zICU+JSAKICBncm91cF9ieShhbmlvLCBtZXMsIGRpYSkgJT4lIAogIHN1bW1hcmlzZSgKICAgIHByaW1lcm8gPSBtaW4oaG9yYXJpb19zYWxpZGEpLAogICAgdWx0aW1vID0gbWF4KGhvcmFyaW9fc2FsaWRhKQogICkKYGBgCgoKYGBge3J9Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgc3VtbWFyaXNlKAogICAgcHJpbWVyYV9zYWxpZGEgPSBmaXJzdChob3JhcmlvX3NhbGlkYSksIAogICAgdWx0aW1hX3NhbGlkYSA9IGxhc3QoaG9yYXJpb19zYWxpZGEpCiAgKQpgYGAKYGBge3J9Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgbXV0YXRlKHIgPSBtaW5fcmFuayhkZXNjKGhvcmFyaW9fc2FsaWRhKSkpICU+JSAKICBmaWx0ZXIociAlaW4lIHJhbmdlKHIpKQpgYGAKCkNvbnRlb3M6IGhhcyB2aXN0byBuKCkKYGBge3J9CiMgwr9RdcOpIGRlc3Rpbm9zIHRpZW5lbiBsYSBtYXlvcsOtYSBkZSBsYXMgYWVyb2zDrW5lYXM/Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGRlc3Rpbm8pICU+JSAKICBzdW1tYXJpc2UoYWVyb2xpbmVhcyA9IG5fZGlzdGluY3QoYWVyb2xpbmVhKSkgJT4lIAogIGFycmFuZ2UoZGVzYyhhZXJvbGluZWFzKSkKYGBgCgpgYGB7cn0Kbm9fY2FuY2VsYWRvcyAlPiUgCiAgY291bnQoZGVzdGlubykKYGBgCgpgYGB7cn0Kbm9fY2FuY2VsYWRvcyAlPiUgCiAgY291bnQoY29kaWdvX2NvbGEsIHd0ID0gZGlzdGFuY2lhKQpgYGAKCk9idGVuZXIgaW5mb3JtYWNpw7NuIGRlIGxvcyB2dWVsb3MgbcOhcyBhdHJhc29uZXMKYGBge3J9Cm5vX2NhbmNlbGFkb3MgJT4lCiAgZ3JvdXBfYnkoYWVyb2xpbmVhLCB2dWVsbywgZGVzdGlubykgJT4lCiAgc3VtbWFyaXNlKG1heF9hdHJhc29zID0gbWF4KGF0cmFzb19zYWxpZGEpLCBtYXhfYXRyYXNvc19sbGVnYWRhID0gbWF4KGF0cmFzb19sbGVnYWRhKSkgJT4lCiAgYXJyYW5nZShkZXNjKG1heF9hdHJhc29zKSwgZGVzYyhtYXhfYXRyYXNvc19sbGVnYWRhKSkgCgoKYGBgCmBgYHtyfQojIMK/Q3XDoW50b3MgdnVlbG9zIHNhbGllcm9uIGFudGVzIGRlIGxhcyA1IGFtPyAKIyAoZXN0b3MgZ2VuZXJhbG1lbnRlIHNvbiB2dWVsb3MgZGVtb3JhZG9zIGRlbCBkw61hIGFudGVyaW9yKQpub19jYW5jZWxhZG9zICU+JSAKICBncm91cF9ieShhbmlvLCBtZXMsIGRpYSkgJT4lIAogIHN1bW1hcmlzZShuX3RlbXByYW5vID0gc3VtKGhvcmFyaW9fc2FsaWRhIDwgNTAwKSkKYGBgCgpgYGB7cn0KIyDCv1F1w6kgcHJvcG9yY2nDs24gZGUgdnVlbG9zIHNlIHJldHJhc2FuIG3DoXMgZGUgdW5hIGhvcmE/Cm5vX2NhbmNlbGFkb3MgJT4lIAogIGdyb3VwX2J5KGFuaW8sIG1lcywgZGlhKSAlPiUgCiAgc3VtbWFyaXNlKGhvcmFfcHJvcCA9IG1lYW4oYXRyYXNvX2xsZWdhZGEgPiA2MCkpCmBgYAo8aDM+NS42LjUgQWdydXBhY2nDs24gcG9yIG3Dumx0aXBsZXMgdmFyaWFibGVzPC9oMz4KYGBge3J9CmRpYXJpbyA8LSBncm91cF9ieSh2dWVsb3MsIGFuaW8sIG1lcywgZGlhKQoocG9yX2RpYSAgIDwtIHN1bW1hcmlzZShkaWFyaW8sIHZ1ZWxvcyA9IG4oKSkpCihwb3JfbWVzIDwtIHN1bW1hcmlzZShwb3JfZGlhLCB2dWVsb3MgPSBzdW0odnVlbG9zKSkpCihwb3JfYW5pbyAgPC0gc3VtbWFyaXNlKHBvcl9tZXMsIHZ1ZWxvcyA9IHN1bSh2dWVsb3MpKSkKCmBgYAo8aDM+NS42LjYgRGVzYWdydXBhcjwvaDM+CmBgYHtyfQpkaWFyaW8gJT4lIAogIHVuZ3JvdXAoKSAlPiUgICAgICAgICAgICAgIyB5YSBubyBlc3TDoSBhZ3J1cGFkbyBwb3IgZmVjaGEKICBzdW1tYXJpc2UodnVlbG9zID0gbigpKSAgICMgdG9kb3MgbG9zIHZ1ZWxvcwpgYGAKCjxoMj41LjcgVHJhbnNmb3JtYWNpb25lcyBhZ3J1cGFkYXMgKHkgZmlsdHJvcyk8L2gyPgpFbmN1ZW50cmEgbG9zIHBlb3JlcyBtaWVtYnJvcyBkZSBjYWRhIGdydXBvOgpgYGB7cn0KdnVlbG9zX3NtbCAlPiUgCiAgZ3JvdXBfYnkoYW5pbywgbWVzLCBkaWEpICU+JQogIGZpbHRlcihyYW5rKGRlc2MoYXRyYXNvX2xsZWdhZGEpKSA8IDEwKQpgYGAKCkVuY3VlbnRyYSB0b2RvcyBsb3MgZ3J1cG9zIG3DoXMgZ3JhbmRlcyBxdWUgdW4gZGV0ZXJtaW5hZG8gdW1icmFsOgpgYGB7cn0KcG9wdWxhcl9kZXN0aW5vcyA8LSB2dWVsb3MgJT4lIAogIGdyb3VwX2J5KGRlc3Rpbm8pICU+JSAKICBmaWx0ZXIobigpID4gMzY1KQpwb3B1bGFyX2Rlc3Rpbm9zCmBgYApFc3RhbmRhcml6YSBwYXJhIGNhbGN1bGFyIGxhcyBtw6l0cmljYXMgcG9yIGdydXBvOgpgYGB7cn0KcG9wdWxhcl9kZXN0aW5vcyAlPiUgCiAgZmlsdGVyKGF0cmFzb19sbGVnYWRhID4gMCkgJT4lIAogIG11dGF0ZShwcm9wX2F0cmFzbyA9IGF0cmFzb19sbGVnYWRhIC8gc3VtKGF0cmFzb19sbGVnYWRhKSkgJT4lIAogIHNlbGVjdChhbmlvOmRpYSwgZGVzdGlubywgYXRyYXNvX2xsZWdhZGEsIHByb3BfYXRyYXNvKQpgYGAKCg==